# = Class ASTAccessMat
# Inherits from ASTExpression. Represents the implementation of an AST especifically designed
# for instructions that make a ternary operation access (v[i,m])
# == Attributes inherited from ASTExpression
# node, children
# == Methods inherited from ASTExpression
# check(), run(), to_s_children(im)
class ASTAccessMat < ASTExpression
  # If the operation is 'v[i,m]', then this operand is 'v'
  attr_reader :lop 
  # If the operation is 'v[i,m]', then this operand is 'i'
  attr_reader :access1
  # If the operation is 'v[i,m]', then this operand is 'm'
  attr_reader :access2
  # Type of element returned by access to matrix
  attr_reader :type
  # Value that indicates both indexes of the range tha's being accessed
  attr_reader :range

  # Constructor of the class
  # === Parameters
  # * lop: If the operation is 'v[i,m]', then this operand is 'v'
  # * access1: If the operation is 'v[i,m]', then this operand is 'i'
  # * access2: If the operation is 'v[i,m]', then this operand is 'm'
  def initialize lop, access1, access2
    @lop = lop
    @access1 = access1
    @access2 = access2
  end

  # Method to evaluate the access to a matrix
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the parent AST
  def eval parent_sym_table, func_table
    msg = @lop.eval parent_sym_table, func_table
    if nil != msg
      return msg
    end
    msg = @access1.eval parent_sym_table, func_table
    if nil != msg
      return msg
    end
    msg = @access2.eval parent_sym_table, func_table
    if nil != msg
      return msg
    end
    if 'mat' == @type

      # Top left delimitation of new matrix
      height = @access1.value.clone
      height[0] = nil != height[0] ? height[0] - 1 : 0
      height[1] = nil != height[1] ? height[1] - 1 : @lop.value.size[0] - 1
  
      if @lop.value.size[0] <= height[0]
        return "Accediendo a la altura '#{@access1.value[0]}' cuando el máximo es #{@lop.value.size[0]} (#{error_location})"
      end
      if @lop.value.size[0] <= height[1]
        return "Accediendo al la altura '#{@access1.value[1]}' cuando el máximo es #{@lop.value.size[0]} (#{error_location})"
      end

      # Bottom right delimitation of new matrix
      width = @access2.value.clone
      width[0] = nil != width[0] ? width[0] - 1 : 0
      width[1] = nil != width[1] ? width[1] - 1 : @lop.value.size[1] - 1
      if @lop.value.size[1] <= width[0]
        return "Accediendo al ancho '#{@access2.value[0]}' cuando el máximo es #{@lop.value.size[1]} (#{error_location})"
      end
      if @lop.value.size[1] <= width[1]
        return "Accediendo al ancho '#{@access2.value[1]}' cuando el máximo es #{@lop.value.size[1]} (#{error_location})"
      end

      lvec = []
      for i in (height[0]..height[1])
        v = []
        for j in (width[0]..width[1])
          # ASTExpression.Matrix.Array[Height][Width]
          v.push @lop.value.value[i][j]
        end
        vec = Vector.new v
        vec.type = 'vec_fil'
        lvec.push vec
      end
      @value = Matrix.new lvec
      @range = [height, width]
    else
      if (@access1.value[0] > @lop.value.value.length) || (@access2.value[0] > @lop.value.value[0].length)
        return "Intentando acceder a elemento [#{@access1.value[0]}, #{@access2.value[0]}], pero el maximo permitido " +
                "es [#{@lop.value.value.length},#{@lop.value.value.length}] (#{error_location})"
      end
      @range = [@access1.value[0] - 1, @access2.value[0] - 1]
      # ASTExpression.Matrix.Array[Height][Width]
      @value = @lop.value.value[@access1.value[0] - 1][@access2.value[0] - 1]
    end
    nil
  end

  
  # Method to check the static correctitude of the access to a matrix
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the pare
  # * func_type: Type of valid return value
  def check parent_sym_table, func_table, func_type
    if nil == @lop
      return "Error interno: ASTAccessMat sin operador izquierdo (#{error_location})"
    end
    if nil == @access1
      return "Error interno: ASTAccessMat sin operador de acceso1 (#{error_location})"
    end
    if nil == @access2
      return "Error interno: ASTAccessMat sin operador de acceso2 (#{error_location})"
    end

    msg = @lop.check parent_sym_table, func_table, func_type
    if nil != msg
      return msg
    end
    msg = @access1.check parent_sym_table, func_table, func_type
    if nil != msg
      return msg
    end
    msg = @access2.check parent_sym_table, func_table, func_type
    if nil != msg
      return msg
    end

    if @lop.type != 'mat'
      return "Tratando de acceder con operador de matriz a '#{@lop.type}' (#{error_location})"
    end
 
    if @access1.type != @access2.type
      return "El operador ternario requiere dos enteros o dos rangos de enteros (#{error_location})"
    end
 
    if 'range' == @access1.type
      @type = 'mat'
    else
      @type = 'num'
    end

    nil
  end

  # Returns the closest wrong token to the place were an error is generated
  def error_tk
    tk = @lop.error_tk
    if nil != tk
      return tk
    end
    tk = nil != @access1 ? @access1.error_tk : nil
    if nil != tk
      return tk
    end
    nil != @access2 ? @access2.error_tk : nil
  end

  # Creates a 'String' representation of the AST with 'i' identation spaces
  # === Parameters
  # * i: Identation with which the AST should be printed with
  def to_s i
    im = i + @@c
    output = "(Acceso de matriz"
    output += "\n#{im}"
    output += @lop.to_s im
    output += "\n#{im}"
    output += @access1.to_s im
    output += "\n#{im}"
    output += @access2.to_s im
    output += "\n#{i})"
  end
end